cluster
predicate - unidirectional graph reachability matrix
#921
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Questions/notes:
1. Is there any documentation whatsoever on how to add new tests? I'd like to add them, but looks like pytest doesn't test in-treestd
, but the install dir2. Is there an autoformatter?
3. I've only added enum versions. Should there only be int versions, and enum versions shall just dispatch tothose?4. I'm not sure about the
multiroot
suffix, should it beanyroot
?5. Also, i'm not sure if
cluster
is the best name? Maybeconstellations
/strongly connected components (scc)
may be more descriptive, i'm not sure.I needed this some time back, and having this
would have saved me quite some time.
Essentially, this ended up consisting of two parts:
reachable_multiroot()
- a seemingly rather boringly straight-forward generalization of thereachable()
, which, instead of enforcing that the subgraph is reachable from the root node (given by index, non-optional), and thus forcing there to be a single joint subgraph, instead takes (zero or more) roots as a mask. The rest of the logic is generalized accordingly.cluster()
predicate. This one took quite a bit of rewrites to fully distill it to it's purest+simplest+fastest form. :) The (final) basic idea is to, basically, give each node a unique "color", and then set the nodes, linked together by selected edges, to the same color, thus ensuring that the nodes, that are not in any way connected, don't have the same "color", and vice versa.Implementation-wise, instead of color, for each node, we track the index of
the disjoint subgraph to which this node belongs, and it turned out
that tracking that by tracking the root node of each subgraph is
the best approach (referred to as root). So there's 3 parts:
reachable_multiroot()
to enfore that each disjoint subgraph actually has a root node, thus different disjoint subgraphs have different roots.Now, this is controversial. I've decided to return the reachability
matrix, NOT the subgraph index. While sparse data structure seems better,
it immediately requires symmetry breaking constraint, which is tricky
(i have written it.), and
--all-solutions
actually ends up being MUCHslower (
O^3
?) than just returning reachability matrix...The directed version would need to return the matrix,
so this is also future-proofing for consistency sake
I have an exhaustive test harness for the
cluster()
(but not the rest), it would not have been possible to refine the predicate so much otherwise, please see https://github.com/LebedevRI/minizinc-graph-reachability-matrix-predicate.mzn/tree/proof-of-concept/unidirectionalRefs. #837